#include "Connection.h"
#include <boost/regex.hpp>
#include "VConsole.h"
#include "Thrd.h"
#include <string.h>
#include <assert.h>
#include "VSocket.h"


using std::string;

#pragma region CLIENT

#ifdef _CLIENT_PART_

Connection::Connection()
{

    m_ClientName_Client = 0;
    m_ServerName_Client = 0;
}

Connection::Connection( const Connection& obj )
{
    assert( !"this kind of constructor is locked for this object" );
}

Connection& Connection::operator=( const Connection& obj )
{
    assert( !"operator = is locked for this object" );
    return *this;
}

bool Connection::StartConnection()
{

    if ( !m_ServerIP_Client.empty() )
    {
        ::WSAStartup( 0x0202, &m_ClientWSADat_Client );                       // winsock init

        m_ClientName_Client = new char[COMPUTER_NAME_LENGTH];
        ::gethostname( m_ClientName_Client, COMPUTER_NAME_LENGTH );

        m_ClientSocketInfo_Client.sin_family = AF_INET;                            // address type
        m_ClientSocketInfo_Client.sin_addr.s_addr = inet_addr( m_ServerIP_Client.c_str() );// server's ip
        m_ClientSocketInfo_Client.sin_port = htons( 2803 );                        // server's port

        m_ClientSocket_Client = ::socket( AF_INET, SOCK_STREAM, 0 );                 // create socket
    }
    else
    {
        VConsole::Instance()->Write( string( "Please input server IP" ), RED );
        return false;
    }

    return true;
}

bool Connection::ConnectToServer()
{
 
    if ( ::connect( m_ClientSocket_Client, (sockaddr*)&m_ClientSocketInfo_Client, sizeof( m_ClientSocketInfo_Client ) ) == SOCKET_ERROR )
    {
        VConsole::Instance()->Write( string( "Connection" ), string( "FAILED" ), RED );
        return false;
    }
    
    m_ServerName_Client = new char[COMPUTER_NAME_LENGTH];

    VSocket sock;

    uint16_t sd = 1;
    
    sock.SetSocket( m_ClientSocket_Client );
    
    sock.Send_char( sd, m_ClientName_Client );
    
    m_ServerName_Client = sock.Recv_char();

    VConsole::Instance()->Write( string( "Connection" ), string( "OK" ), GREEN );

    VConsole::Instance()->Write( string( "Connection with " ) + string( m_ServerName_Client ) + string( " is established." ) );

    return true;
}

bool Connection::DisconnectFromServer ()
{

    ::closesocket( m_ClientSocket_Client );
    ::WSACleanup();

    return true;
}

bool Connection::SetIP( std::string& ip )
{
   /* //check for correct ip
    boost::regex expression("");//(^[012](?(?<=[01])[\d]|[0-5])(?(?<=[01][\d])[\d]|[0-5])\.[012](?(?<=[01])[\d]|[0-5])(?(?<=[01][\d])[\d]|[0-5])\.[012](?(?<=[01])[\d]|[0-5])(?(?<=[01][\d])[\d]|[0-5])\.[012](?(?<=[01])[\d]|[0-5])(?(?<=[01][\d])[\d]|[0-5])$)");
  //([012](?(?<=[01])[\d]|[0-5])(?(?<=[01][\d])[\d]|[0-5]))
    boost::cmatch what;

    if( regex_search( ip.c_str(), what, expression ) )
    {
        m_ServerIP_Client = what[1];   
        std::cout << m_ServerIP_Client;
        std::cout << what[2]; 

        return true;
    }
    else
    {
        VConsole::Instance()->Write( string( "Bad IP" ), RED );

        return false;
    }*/
    m_ServerIP_Client = ip;
    return true;
}

Connection::~Connection()
{
    delete[] m_ClientName_Client;
    delete[] m_ServerName_Client;
}

bool Connection::PingServer()
{
    std::string execCommand = std::string( "ping " ) + m_ServerIP_Client + std::string ( ">nul" );
    
    char *cmd = new char[execCommand.size()];
    strcpy( cmd, execCommand.c_str());
    
    // execute command
    // 0 - OK
    // 1 - bad ip
    
    Thrd oneThread;
    oneThread.CreateThread( "Ping server", &( VConsole::WriteWaiting ) );

    VConsole::stop = true;
    int ret = ::system( cmd );
    VConsole::stop = false;
    oneThread.KillThread();

    delete[] cmd;

    if ( ret == 0 )
    {
        VConsole::Instance()->Write( string( "Connection" ), string( "OK" ), GREEN );
        return true;
    }     
    else
    {
        VConsole::Instance()->Write( string( "Connection" ), string( "FAILED" ), RED );
        return false;
    }
}


#endif

#pragma endregion

#pragma region SERVER

#ifdef _SERVER_PART_

Connection::Connection()
{
    m_ClientName_Serv = 0;
    m_ServerName_Serv = 0;  
 
}

Connection::Connection( const Connection& obj )
{
    assert( !"this kind of constructor is locked for this object" );
}

Connection& Connection::operator=( const Connection& obj )
{
    assert( !"operator = is locked for this object" );
    return *this;
}

bool Connection::StartConnection()
{
    ::WSAStartup( 0x0202, &m_ServerWSADat_Serv );                                  // winsock init

    m_ServerName_Serv = new char[COMPUTER_NAME_LENGTH];
    ::gethostname( m_ServerName_Serv, COMPUTER_NAME_LENGTH );                      // get client name
    
    m_ServerSocketInfo_Serv.sin_family = AF_INET;                                // address type
    m_ServerSocketInfo_Serv.sin_addr.s_addr = 0;
    m_ServerSocketInfo_Serv.sin_port = htons( 2803 );                            // port
    m_ServerSocket_Serv = ::socket( AF_INET, SOCK_STREAM, IPPROTO_TCP );           // create socket
    
    if ( ::bind( m_ServerSocket_Serv, (sockaddr*)&m_ServerSocketInfo_Serv, sizeof( m_ServerSocketInfo_Serv ) ) ) return false;

    return true;
}

bool Connection::ConnectToClient()
{
    ::listen( m_ServerSocket_Serv, SOMAXCONN );

    m_ClientSocket_Serv = ::accept( m_ServerSocket_Serv, (sockaddr*)&m_ServerSocketInfo_Serv, 0 );


    VSocket sock;

    uint16_t testType = 1;

    sock.SetSocket( m_ClientSocket_Serv );

    m_ClientName_Serv = sock.Recv_char();

    sock.Send_char( testType, m_ServerName_Serv );

    VConsole::Instance()->Write( string( "Connection" ), string( "OK" ), GREEN );

    VConsole::Instance()->Write( string( "Connection with " ) + string( m_ClientName_Serv ) + string( " is established." ) );

    
    return true;
}

bool Connection::DisconnectFromClient()
{
    ::closesocket( m_ServerSocket_Serv );
    ::closesocket( m_ClientSocket_Serv );
    ::WSACleanup();

    return true;
}

Connection::~Connection()
{
    delete[] m_ClientName_Serv;
    delete[] m_ServerName_Serv;  
}


#endif

#pragma endregion